import domain.Student;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;

/**
 * CompletableFutureTest描述
 * <p>
 * 依赖关系
 * thenApply()：把前面任务的执行结果，交给后面的Function
 * thenCompose()：用来连接两个有依赖关系的任务，结果由第二个任务返回
 * and集合关系
 * thenCombine()：合并任务，有返回值
 * thenAccepetBoth()：两个任务执行完成后，将结果交给thenAccepetBoth处理，无返回值
 * runAfterBoth()：两个任务都执行完成后，执行下一步操作(Runnable类型任务)
 * or聚合关系
 * applyToEither()：两个任务哪个执行的快，就使用哪一个结果，有返回值
 * acceptEither()：两个任务哪个执行的快，就消费哪一个结果，无返回值
 * runAfterEither()：任意一个任务执行完成，进行下一步操作(Runnable类型任务)
 * 并行执行
 * allOf()：当所有给定的 CompletableFuture 完成时，返回一个新的 CompletableFuture
 * anyOf()：当任何一个给定的CompletablFuture完成时，返回一个新的CompletableFuture
 * 结果处理
 * whenComplete：当任务完成时，将使用结果(或 null)和此阶段的异常(或 null如果没有)执行给定操作
 * exceptionally：返回一个新的CompletableFuture，当前面的CompletableFuture完成时，它也完成，当它异常完成时，给定函数的异常触发这个CompletableFuture的完成
 *
 * @author Administrator-YHL
 * @date 2022-11-21
 **/
public class CompletableFutureTest {
    /**
     * CompletableFuture提供了四个静态方法来创建一个异步操作：
     * <p>
     * public static CompletableFuture<Void> runAsync(Runnable runnable)
     * public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
     * public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
     * public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
     * 1
     * 2
     * 3
     * 4
     * 这四个方法的区别：
     * <p>
     * runAsync() 以Runnable函数式接口类型为参数，没有返回结果，supplyAsync() 以Supplier函数式接口类型为参数，返回结果类型为U；Supplier接口的 get()是有返回值的(会阻塞)
     * 使用没有指定Executor的方法时，内部使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池，则使用指定的线程池运行。
     * 默认情况下CompletableFuture会使用公共的ForkJoinPool线程池，这个线程池默认创建的线程数是 CPU 的核数（也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置ForkJoinPool线程池的线程数）。如果所有CompletableFuture共享一个线程池，那么一旦有任务执行一些很慢的 I/O 操作，就会导致线程池中所有线程都阻塞在 I/O 操作上，从而造成线程饥饿，进而影响整个系统的性能。所以，强烈建议你要根据不同的业务类型创建不同的线程池，以避免互相干扰
     *
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
       /* Runnable runnable = () -> System.out.println("无返回结果异步任务");
        CompletableFuture.runAsync(runnable);

        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("有返回值的异步任务");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello World";
        });
        String result = future.get();


        System.out.println(result);
        //任务1：洗水壶->烧开水
        CompletableFuture<Void> f1 = CompletableFuture
                .runAsync(() -> {
                    System.out.println("T1:洗水壶...");
                    sleep(1, TimeUnit.SECONDS);

                    System.out.println("T1:烧开水...");
                    sleep(15, TimeUnit.SECONDS);
                });
        //任务2：洗茶壶->洗茶杯->拿茶叶
        CompletableFuture<String> f2 = CompletableFuture
                .supplyAsync(() -> {
                    System.out.println("T2:洗茶壶...");
                    sleep(1, TimeUnit.SECONDS);

                    System.out.println("T2:洗茶杯...");
                    sleep(2, TimeUnit.SECONDS);

                    System.out.println("T2:拿茶叶...");
                    sleep(1, TimeUnit.SECONDS);
                    return "龙井";
                });
        //任务3：任务1和任务2完成后执行：泡茶
        CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf) -> {
            System.out.println("T1:拿到茶叶:" + tf);
            System.out.println("T1:泡茶...");
            return "上茶:" + tf;
        });
        //等待任务3执行结果
        System.out.println(f3.join());*/


        /*CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
            if (new SecureRandom().nextInt(10) % 2 == 0) {
                int i = 12 / 0;
            }
            System.out.println("执行结束！");
            return "test";
        });
        // 任务完成或异常方法完成时执行该方法
        // 如果出现了异常,任务结果为null
        future1.whenComplete((s, t) -> System.out.println(s + " 执行完成！"));
        // 出现异常时先执行该方法
        future1.exceptionally(new Function<Throwable, String>() {
            @Override
            public String apply(Throwable t) {
                System.out.println("执行失败：" + t.getMessage());
                return "异常xxxx";
            }
        });

        String future1r = future1.get();
        System.out.println(future1r);*/

        /**
         * 看特了博飞雀
         *
         * thenCompose 可以用于组合多个CompletableFuture，将前一个任务的返回结果作为下一个任务的参数，它们之间存在着业务逻辑上的先后顺序。
         * 方法会在某个任务执行完成后，将该任务的执行结果作为方法入参然后执行指定的方法，该方法会返回一个新的CompletableFuture实例
         */
        CompletableFuture<Student> studentCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Student student = new Student();
            student.setName(Thread.currentThread().getName() + "线程加入");
            student.setAge(18);
            student.setGender(false);
            student.setGrade("男");
            return student;
        }).thenCompose(student -> CompletableFuture.supplyAsync(() -> {
            //注意这里用到了上个线程的返回值 student
            student.setName(Thread.currentThread().getName() + "-----" + student.getName());
            System.out.println("线程打印:" + Thread.currentThread().getName() + "--" + student.getName());
            return student;
        }));
        System.out.println(studentCompletableFuture.get().toString());

        /**
         * thenCombine 会在两个任务都执行完成后，把两个任务的结果合并
         * 注意：
         *
         * 两个任务中只要有一个执行异常，则将该异常信息作为指定任务的执行结果。
         * 两个任务是并行执行的，它们之间并没有先后依赖顺序。
         */
        // 并行
        CompletableFuture<Student> studentCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
            Student student = new Student();
            student.setName(Thread.currentThread().getName() + "赋值");
            System.out.println(Thread.currentThread().getName() + "运行时:" + System.currentTimeMillis());
            return student;
        }).whenComplete((s, t) -> {
            // 任务完成或异常方法完成时执行该方法
            // 如果出现了异常,任务结果为null
            System.out.println(s + " 执行完成！");
        }).exceptionally(ex -> {
            // 当子线程抛出异常时，将回调该方法，完成降级，该方法有返回值
            System.out.println(ex.toString());
            return new Student();
        });
        CompletableFuture<Student> studentCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
            Student student = new Student();
            student.setName(Thread.currentThread().getName() + "赋值");
            System.out.println(Thread.currentThread().getName() + "运行时:" + System.currentTimeMillis());
            return student;
        });
        CompletableFuture<List<Student>> completableFuture = studentCompletableFuture1.thenCombine(studentCompletableFuture2
                , new BiFunction<Student, Student, List<Student>>() {
                    @Override
                    public List<Student> apply(Student student, Student student2) {
                        List<Student> studentList = new ArrayList<>();
                        studentList.add(student);
                        studentList.add(student2);
                        return studentList;
                    }
                });
        System.out.println(completableFuture.get().toString());
    }

    public static void sleep(int time, TimeUnit unit) {
        try {
            unit.sleep(time);
        } catch (InterruptedException e) {
        }
    }
}
